home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / infoserv / www / cern / proxy-support / Mosaic-2.4 / HTTP.c.Z / HTTP.c
Encoding:
C/C++ Source or Header  |  1994-05-11  |  20.9 KB  |  733 lines

  1. /*    HyperText Tranfer Protocol    - Client implementation        HTTP.c
  2. **    ==========================
  3. */
  4.  
  5. #include "HTTP.h"
  6.  
  7. #define HTTP_VERSION    "HTTP/1.0"
  8.  
  9. #define INIT_LINE_SIZE        1024    /* Start with line buffer this big */
  10. #define LINE_EXTEND_THRESH    256    /* Minimum read size */
  11. #define VERSION_LENGTH         20    /* for returned protocol version */
  12.  
  13. #include "HTParse.h"
  14. #include "HTUtils.h"
  15. #include "tcp.h"
  16. #include "HTTCP.h"
  17. #include "HTFormat.h"
  18. #include "HTFile.h"
  19. #include <ctype.h>
  20. #include "HTAlert.h"
  21. #include "HTMIME.h"
  22. #include "HTML.h"
  23. #include "HTInit.h"
  24. #include "HTAABrow.h"
  25.  
  26. /* #define TRACE 1 */
  27.  
  28. struct _HTStream 
  29. {
  30.   HTStreamClass * isa;
  31. };
  32.  
  33. extern char * HTAppName;    /* Application name: please supply */
  34. extern char * HTAppVersion;    /* Application version: please supply */
  35.  
  36. /* Variables that control whether we do a POST or a GET,
  37.    and if a POST, what and how we POST. */
  38. int do_post = 0;
  39. char *post_content_type = NULL;
  40. char *post_data = NULL;
  41. extern BOOL using_gateway;    /* are we using an HTTP gateway? */
  42. extern BOOL using_proxy;      /* are we using an HTTP proxy gateway? */
  43. PUBLIC BOOL reloading = NO;   /* reloading => send no-cache pragma to proxy */
  44.  
  45. /*        Load Document from HTTP Server            HTLoadHTTP()
  46. **        ==============================
  47. **
  48. **    Given a hypertext address, this routine loads a document.
  49. **
  50. **
  51. ** On entry,
  52. **    arg    is the hypertext reference of the article to be loaded.
  53. **
  54. ** On exit,
  55. **    returns    >=0    If no error, a good socket number
  56. **        <0    Error.
  57. **
  58. **    The socket must be closed by the caller after the document has been
  59. **    read.
  60. **
  61. */
  62. PUBLIC int HTLoadHTTP ARGS4 (
  63.     char *,         arg,
  64.     HTParentAnchor *,    anAnchor,
  65.     HTFormat,        format_out,
  66.     HTStream*,        sink)
  67. {
  68.   int s;                /* Socket number for returned data */
  69.   char *command;            /* The whole command */
  70.   char *eol;            /* End of line if found */
  71.   char *start_of_data;        /* Start of body of reply */
  72.   int status;                /* tcp return */
  73.   int bytes_already_read;
  74.   char crlf[3];            /* A CR LF equivalent string */
  75.   HTStream *target;        /* Unconverted data */
  76.   HTFormat format_in;            /* Format arriving in the message */
  77.   
  78.   BOOL had_header;        /* Have we had at least one header? */
  79.   char *line_buffer;
  80.   char *line_kept_clean;
  81.   BOOL extensions;        /* Assume good HTTP server */
  82.   int compressed;
  83.   char line[256];
  84.   
  85.   int length, doing_redirect, rv;
  86.   int already_retrying = 0;
  87.   int return_nothing;
  88.  
  89. #ifdef PEM_AUTH
  90.   BOOL doing_pem;
  91.   char *body;
  92.   int body_size;
  93. #endif /* PEM_AUTH */
  94.   
  95.   if (!arg)
  96.     {
  97.       status = -3;
  98.       HTProgress ("Bad request.");
  99.       goto done;
  100.     }
  101.   if (!*arg) 
  102.     {
  103.       status = -2;
  104.       HTProgress ("Bad request.");
  105.       goto done;
  106.     }
  107.   
  108.   sprintf(crlf, "%c%c", CR, LF);
  109.  
  110.   /* At this point, we're talking HTTP/1.0. */
  111.   extensions = YES;
  112.  
  113.  try_again:
  114.   /* All initializations are moved down here from up above,
  115.      so we can start over here... */
  116.   eol = 0;
  117.   bytes_already_read = 0;
  118.   had_header = NO;
  119.   length = 0;
  120.   doing_redirect = 0;
  121.   compressed = 0;
  122.   target = NULL;
  123.   line_buffer = NULL;
  124.   line_kept_clean = NULL;
  125.   return_nothing = 0;
  126. #ifdef PEM_AUTH
  127.   doing_pem = NO;
  128. #endif /* PEM_AUTH */
  129.  
  130.   status = HTDoConnect (arg, "HTTP", TCP_PORT, &s);
  131.   if (status == HT_INTERRUPTED)
  132.     {
  133.       /* Interrupt cleanly. */
  134.       if (TRACE)
  135.         fprintf (stderr,
  136.                  "HTTP: Interrupted on connect; recovering cleanly.\n");
  137.       HTProgress ("Connection interrupted.");
  138.       /* status already == HT_INTERRUPTED */
  139.       goto done;
  140.     }
  141.   if (status < 0) 
  142.     {
  143.       if (TRACE) 
  144.         fprintf(stderr, 
  145.                 "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, errno);
  146.       HTProgress ("Unable to connect to remote host.");
  147.       status = HT_NO_DATA;
  148.       goto done;
  149.     }
  150.   
  151.   /*    Ask that node for the document,
  152.    **    omitting the host name & anchor
  153.    */        
  154.   {
  155.     char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
  156.     command = malloc(5 + strlen(p1)+ 2 + 31);
  157.  
  158.     if (do_post)
  159.       strcpy(command, "POST ");
  160.     else
  161.       strcpy(command, "GET ");
  162.  
  163.     /*
  164.      * For a gateway, the beginning '/' on the request must
  165.      * be stripped before appending to the gateway address.
  166.      */
  167.     if ((using_gateway)||(using_proxy))
  168.         strcat(command, p1+1);
  169.     else
  170.         strcat(command, p1);
  171.     free(p1);
  172.   }
  173.   if (extensions) 
  174.     {
  175.       strcat(command, " ");
  176.       strcat(command, HTTP_VERSION);
  177.     }
  178.   
  179.   strcat(command, crlf);    /* CR LF, as in rfc 977 */
  180.   
  181.   if (extensions) 
  182.     {
  183.       int n, i;
  184.       
  185.       if (!HTPresentations) HTFormatInit();
  186.       n = HTList_count(HTPresentations);
  187.       
  188.       for(i=0; i<n; i++) 
  189.         {
  190.           HTPresentation * pres = HTList_objectAt(HTPresentations, i);
  191.           if (pres->rep_out == WWW_PRESENT) 
  192.             {
  193.               sprintf(line, "Accept: %s%c%c",
  194.                       HTAtom_name(pres->rep), CR, LF);
  195.               StrAllocCat(command, line);
  196.             }
  197.         }
  198.  
  199.       /*
  200.        * When reloading give no-cache pragma to proxy server to make
  201.        * it refresh its cache. -- Ari L. <luotonen@dxcern.cern.ch>
  202.        */
  203.       if (reloading) {
  204.       sprintf(line, "Pragma: no-cache%c%c", CR, LF);
  205.       StrAllocCat(command, line);
  206.       }
  207.  
  208.       sprintf(line, "User-Agent:  %s/%s  libwww/%s%c%c",
  209.               HTAppName ? HTAppName : "unknown",
  210.               HTAppVersion ? HTAppVersion : "0.0",
  211.               HTLibraryVersion, CR, LF);
  212.       StrAllocCat(command, line);
  213.       
  214.       {
  215.         char *docname;
  216.         char *hostname;
  217.         char *colon;
  218.         int portnumber;
  219.         char *auth;
  220.         
  221.         docname = HTParse(arg, "", PARSE_PATH);
  222.         hostname = HTParse(arg, "", PARSE_HOST);
  223.         if (hostname &&
  224.             NULL != (colon = strchr(hostname, ':'))) 
  225.           {
  226.             *(colon++) = '\0';    /* Chop off port number */
  227.             portnumber = atoi(colon);
  228.           }
  229.         else portnumber = 80;
  230.         
  231.         if (NULL!=(auth=HTAA_composeAuth(hostname, portnumber, docname))) 
  232.           {
  233.             sprintf(line, "%s%c%c", auth, CR, LF);
  234.             StrAllocCat(command, line);
  235.           }
  236.         if (TRACE) 
  237.           {
  238.             if (auth)
  239.               fprintf(stderr, "HTTP: Sending authorization: %s\n", auth);
  240.             else
  241.               fprintf(stderr, "HTTP: Not sending authorization (yet)\n");
  242.           }
  243.         FREE(hostname);
  244.         FREE(docname);
  245.       }
  246.     }
  247.  
  248.   if (do_post)
  249.     {
  250.       if (TRACE)
  251.         fprintf (stderr, "HTTP: Doing post, content-type '%s'\n",
  252.                  post_content_type);
  253.       sprintf (line, "Content-type: %s%c%c",
  254.                post_content_type ? post_content_type : "lose", CR, LF);
  255.       StrAllocCat(command, line);
  256.       {
  257.         int content_length;
  258.         if (!post_data)
  259.           content_length = 4; /* 4 == "lose" :-) */
  260.         else
  261.           content_length = strlen (post_data);
  262.         sprintf (line, "Content-length: %d%c%c",
  263.                  content_length, CR, LF);
  264.         StrAllocCat(command, line);
  265.       }
  266.       
  267.       StrAllocCat(command, crlf);    /* Blank line means "end" */
  268.       
  269.       if (post_data)
  270.         StrAllocCat(command, post_data);
  271.       else
  272.         StrAllocCat(command, "lose");
  273.     }
  274.  
  275.   StrAllocCat(command, crlf);    /* Blank line means "end" */
  276.  
  277. #ifdef PEM_AUTH
  278. /*
  279.  * HTAA_makecommand will check if we're doing PEM authentication, and if
  280.  * so, will return a new command which is actually the command which was
  281.  * just built, encrypted, and placed inside a dummy HTTP request. Body
  282.  * is kept separate from the command because in the case of PGP data, the
  283.  * message body can be binary.
  284.  */
  285.  
  286.   body = NULL;
  287.   body_size = -1;
  288.   command = HTAA_makecommand(command,&body,&body_size);
  289. #endif /* PEM_AUTH */
  290.   
  291.   if (TRACE)
  292.     fprintf (stderr, "Writing:\n%s----------------------------------\n",
  293.              command);
  294.   
  295.   HTProgress ("Sending HTTP request.");
  296.  
  297.   status = NETWRITE(s, command, (int)strlen(command));
  298. #ifdef PEM_AUTH
  299.   if(body) {
  300.       status = NETWRITE(s, body, body_size);
  301.       free(body);
  302.   }
  303. #endif /* PEM_AUTH */
  304.   free (command);
  305.   if (status <= 0) 
  306.     {
  307.       if (status == 0)
  308.         {
  309.           if (TRACE)
  310.             fprintf (stderr, "HTTP: Got status 0 in initial write\n");
  311.           /* Do nothing. */
  312.         }
  313.       else if 
  314.         ((errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE) &&
  315.          !already_retrying &&
  316.          /* Don't retry if we're posting. */ !do_post)
  317.           {
  318.             /* Arrrrgh, HTTP 0/1 compability problem, maybe. */
  319.             if (TRACE)
  320.               fprintf 
  321.                 (stderr, 
  322.                  "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n");
  323.             HTProgress ("Retrying as HTTP0 request.");
  324.             NETCLOSE(s);
  325.             extensions = NO;
  326.             already_retrying = 1;
  327.             goto try_again;
  328.           }
  329.       else
  330.         {
  331.           if (TRACE)
  332.             fprintf (stderr, "HTTP: Hit unexpected network WRITE error; aborting connection.\n");
  333.           NETCLOSE (s);
  334.           status = -1;
  335.           HTProgress ("Unexpected network write error; connection aborted.");
  336.           goto done;
  337.         }
  338.     }
  339.   
  340.   if (TRACE)
  341.     fprintf (stderr, "HTTP: WRITE delivered OK\n");
  342.   HTProgress ("Done sending HTTP request; waiting for response.");
  343.  
  344.   /*    Read the first line of the response
  345.    **    -----------------------------------
  346.    */
  347.  
  348. #ifdef PEM_AUTH
  349. /* 
  350.  * reparse: We got an encapsulated response from the server, and have 
  351.  * decrypted it. Now we restart the whole process with the decrypted 
  352.  * response.
  353.  */
  354.  reparse:
  355.   length=0;
  356.   bytes_already_read=0;
  357.   eol = 0;
  358. #endif /* PEM_AUTH */ 
  359.   {
  360.     /* Get numeric status etc */
  361.     BOOL end_of_file = NO;
  362.     HTAtom * encoding = HTAtom_for("8bit");
  363.     int buffer_length = INIT_LINE_SIZE;
  364.     
  365.     line_buffer = (char *) malloc(buffer_length * sizeof(char));
  366.     
  367.     do 
  368.       {    /* Loop to read in the first line */
  369.         /* Extend line buffer if necessary for those crazy WAIS URLs ;-) */
  370.         if (buffer_length - length < LINE_EXTEND_THRESH) 
  371.           {
  372.             buffer_length = buffer_length + buffer_length;
  373.             line_buffer = 
  374.               (char *) realloc(line_buffer, buffer_length * sizeof(char));
  375.           }
  376.         if (TRACE)
  377.           fprintf (stderr, "HTTP: Trying to read %d\n",
  378.                    buffer_length - length - 1);
  379.         status = NETREAD(s, line_buffer + length,
  380.                          buffer_length - length - 1);
  381.         if (TRACE)
  382.           fprintf (stderr, "HTTP: Read %d\n", status);
  383.         if (status <= 0) 
  384.           {
  385.             /* Retry if we get nothing back too; 
  386.                bomb out if we get nothing twice. */
  387.             if (status == HT_INTERRUPTED)
  388.               {
  389.                 if (TRACE)
  390.                   fprintf (stderr, "HTTP: Interrupted initial read.\n");
  391.                 HTProgress ("Connection interrupted.");
  392.                 status = HT_INTERRUPTED;
  393.         NETCLOSE (s);
  394.                 goto clean_up;
  395.               }
  396.             else if 
  397.               (status < 0 &&
  398.                (errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE) &&
  399.                !already_retrying &&
  400.                !do_post)
  401.               {
  402.                 /* Arrrrgh, HTTP 0/1 compability problem, maybe. */
  403.                 if (TRACE)
  404.                   fprintf (stderr, "HTTP: BONZO Trying again with HTTP0 request.\n");
  405.                 NETCLOSE(s);
  406.                 if (line_buffer) 
  407.                   free(line_buffer);
  408.                 if (line_kept_clean) 
  409.                   free(line_kept_clean);
  410.                 
  411.                 extensions = NO;
  412.                 already_retrying = 1;
  413.                 HTProgress ("Retrying as HTTP0 request.");
  414.                 goto try_again;
  415.               }
  416.             else
  417.               {
  418.                 if (TRACE)
  419.                   fprintf (stderr, "HTTP: Hit unexpected network read error; aborting connection; status %d.\n", status);
  420.                 HTProgress 
  421.                   ("Unexpected network read error; connection aborted.");
  422.  
  423.                 NETCLOSE (s);
  424.                 status = -1;
  425.                 goto clean_up;
  426.               }
  427.           }
  428.  
  429.         bytes_already_read += status;
  430.         {
  431.           char line[256];
  432.           sprintf (line, "Read %d bytes of data.", bytes_already_read);
  433.           HTProgress (line);
  434.         }
  435.         
  436.         if (status == 0) 
  437.           {
  438.             end_of_file = YES;
  439.             break;
  440.           }
  441.         line_buffer[length+status] = 0;
  442.         
  443.         if (line_buffer)
  444.           {
  445.             if (line_kept_clean)
  446.               free (line_kept_clean);
  447.             line_kept_clean = (char *)malloc (buffer_length * sizeof (char));
  448.             bcopy (line_buffer, line_kept_clean, buffer_length);
  449.           }
  450.         
  451.         eol = strchr(line_buffer + length, LF);
  452.         /* Do we *really* want to do this? */
  453.         if (eol && eol != line_buffer && *(eol-1) == CR) 
  454.           *(eol-1) = ' '; 
  455.         
  456.         length = length + status;
  457.  
  458.         /* Do we really want to do *this*? */
  459.         if (eol) 
  460.           *eol = 0;        /* Terminate the line */
  461.       }
  462.     /* All we need is the first line of the response.  If it's a HTTP/1.0
  463.        response, then the first line will be absurdly short and therefore
  464.        we can safely gate the number of bytes read through this code
  465.        (as opposed to below) to ~1000. */
  466.     /* Well, let's try 100. */
  467.     while (!eol && !end_of_file && bytes_already_read < 100);
  468.   } /* Scope of loop variables */
  469.     
  470.     
  471.   /*    We now have a terminated unfolded line. Parse it.
  472.    **    -------------------------------------------------
  473.    */
  474.   if (TRACE)
  475.     fprintf(stderr, "HTTP: Rx: %s\n", line_buffer);
  476.   
  477.   {
  478.     int fields;
  479.     char server_version[VERSION_LENGTH+1];
  480.     int server_status;
  481.  
  482.     server_version[0] = 0;
  483.     
  484.     fields = sscanf(line_buffer, "%20s %d",
  485.                     server_version,
  486.                     &server_status);
  487.     
  488.     if (TRACE)
  489.       fprintf (stderr, "HTTP: Scanned %d fields from line_buffer\n", fields);
  490.     if (TRACE)
  491.       fprintf (stderr, "HTTP: line_buffer '%s'\n", line_buffer);
  492.     
  493.     /* Rule out HTTP/1.0 reply as best we can. */
  494.     if (fields < 2 || !server_version[0] || server_version[0] != 'H' ||
  495.         server_version[1] != 'T' || server_version[2] != 'T' ||
  496.         server_version[3] != 'P' || server_version[4] != '/' ||
  497.         server_version[6] != '.') 
  498.       {    
  499.         /* HTTP0 reply */
  500.         HTAtom * encoding;
  501.  
  502.         if (TRACE)
  503.           fprintf (stderr, "--- Talking HTTP0.\n");
  504.         
  505.         format_in = HTFileFormat(arg, &encoding, WWW_HTML, &compressed);
  506.         start_of_data = line_kept_clean;
  507.       } 
  508.     else 
  509.       {
  510.         /* Decode full HTTP response */
  511.         format_in = HTAtom_for("www/mime");
  512.         /* We set start_of_data to "" when !eol here because there
  513.            will be a put_block done below; we do *not* use the value
  514.            of start_of_data (as a pointer) in the computation of
  515.            length or anything else in this situation. */
  516.         start_of_data = eol ? eol + 1 : "";
  517.         length = eol ? length - (start_of_data - line_buffer) : 0;
  518.         
  519.         if (TRACE)
  520.           fprintf (stderr, "--- Talking HTTP1.\n");
  521.         
  522.         switch (server_status / 100) 
  523.           {
  524.           case 3:        /* Various forms of redirection */
  525.             /* We now support this in the parser, at least. */
  526.             doing_redirect = 1;
  527.             break;
  528.             
  529.           case 4:        /* "I think I goofed" */
  530.             switch (server_status) 
  531.               {
  532.               case 403:
  533.                 /* 403 is "forbidden"; display returned text. */
  534.                 break;
  535.  
  536.               case 401:
  537.                 /* length -= start_of_data - text_buffer; */
  538.                 if (HTAA_shouldRetryWithAuth(start_of_data, length, s)) 
  539.                   {
  540.                     (void)NETCLOSE(s);
  541.                     if (line_buffer) 
  542.                       free(line_buffer);
  543.                     if (line_kept_clean) 
  544.                       free(line_kept_clean);
  545.  
  546.                     if (TRACE) 
  547.                       fprintf(stderr, "%s %d %s\n",
  548.                               "HTTP: close socket", s,
  549.                               "to retry with Access Authorization");
  550.                     
  551.                     HTProgress ("Retrying with access authorization information.");
  552.                     goto try_again;
  553.                     break;
  554.                   }
  555.                 else 
  556.                   {
  557.                     /* Fall through. */
  558.                   }
  559.  
  560.               default:
  561.                 break;
  562.               } /* case 4 switch */
  563.             break;
  564.  
  565.           case 5:        /* I think you goofed */
  566.             break;
  567.             
  568.           case 2:        /* Good: Got MIME object */
  569.             switch (server_status)
  570.               {
  571.               case 204:
  572.                 return_nothing = 1;
  573.                 format_in = HTAtom_for("text/html");
  574.                 break;
  575.               default:
  576. #ifdef PEM_AUTH
  577. /*
  578.  * HTAA_switchPEMresponse will return us a new file descriptor if we should
  579.  * re-parse the response, will return -1 if some error occured, and will 
  580.  * return -2 if there was no PEM going on at the time.
  581.  */
  582.                 {
  583.                     int s2;
  584.                     if(doing_pem == NO) {
  585.                         if((s2 = HTAA_switchPEMresponse(start_of_data, length, s)) == -1) {
  586.                             HTProgress("An error occured while decrypting.");
  587.                             status = -1;
  588.                 NETCLOSE (s);
  589.                             goto clean_up;
  590.                         }
  591.  
  592.             if (s2 == -3)
  593.             {
  594.                 HTAA_ClearAuth();
  595.                 (void)NETCLOSE(s);
  596.                 if (line_buffer) 
  597.                   free(line_buffer);
  598.                 if (line_kept_clean) 
  599.                   free(line_kept_clean);
  600.  
  601.                 if (TRACE) 
  602.                   fprintf(stderr, "%s %d %s\n",
  603.                       "HTTP: close socket", s,
  604.                       "to retry without Access Authorization");
  605.                     
  606.                 HTProgress ("Retrying without access authorization information.");
  607.                 goto try_again;
  608.             }
  609.  
  610.                         if(s2 != -2) {
  611.                             HTProgress("Got encrypted response, decrypting.");
  612.                             doing_pem = YES;
  613.                             s = s2;
  614.                             goto reparse;
  615.                         }
  616.                     }
  617.                 }
  618. #endif /* PEM_AUTH */
  619.                 break;
  620.               }
  621.             break;
  622.             
  623.           default:        /* bad number */
  624.             HTAlert("Unknown status reply from server!");
  625.             break;
  626.           } /* Switch on server_status/100 */
  627.         
  628.       }    /* Full HTTP reply */
  629.   } /* scope of fields */
  630.  
  631.   /* Set up the stream stack to handle the body of the message */
  632.   target = HTStreamStack(format_in,
  633.                          format_out,
  634.                          compressed,
  635.                          sink, anAnchor);
  636.   
  637.   if (!target) 
  638.     {
  639.       char buffer[1024];    /* @@@@@@@@ */
  640.       sprintf(buffer, "Sorry, no known way of converting %s to %s.",
  641.               HTAtom_name(format_in), HTAtom_name(format_out));
  642.       HTProgress (buffer);
  643.       status = -1;
  644.       NETCLOSE (s);
  645.       goto clean_up;
  646.     }
  647.  
  648.   if (!return_nothing)
  649.     {
  650.       if (TRACE)
  651.         fprintf (stderr, "HTTP: Doing put_block, '%s'\n", start_of_data);
  652.       /* Recycle the first chunk of data, in all cases. */
  653.       (*target->isa->put_block)(target, start_of_data, length);
  654.       
  655.       /* Go pull the bulk of the data down. */
  656.       rv = HTCopy(s, target, bytes_already_read);
  657.       if (rv == -1)
  658.         {
  659.           (*target->isa->handle_interrupt) (target);
  660.           status = HT_INTERRUPTED;
  661.       NETCLOSE (s);
  662.           goto clean_up;
  663.         }
  664.       if (rv == -2 && !already_retrying && !do_post)
  665.         {
  666.           /* Aw hell. */
  667.           if (TRACE)
  668.             fprintf (stderr, "HTTP: Trying again with HTTP0 request.\n");
  669.           /* May as well consider it an interrupt -- right? */
  670.           (*target->isa->handle_interrupt) (target);
  671.           NETCLOSE(s);
  672.           if (line_buffer) 
  673.             free(line_buffer);
  674.           if (line_kept_clean) 
  675.             free(line_kept_clean);
  676.           
  677.           extensions = NO;
  678.           already_retrying = 1;
  679.           HTProgress ("Retrying as HTTP0 request.");
  680.           goto try_again;
  681.         }
  682.     }
  683.   else
  684.     {
  685.       /* return_nothing is high. */
  686.       (*target->isa->put_string) (target, "<mosaic-access-override>\n");
  687.       HTProgress ("And silence filled the night.");
  688.     }
  689.  
  690.   (*target->isa->end_document)(target);
  691.  
  692. #ifdef PEM_AUTH
  693.   /* If we're processing a PEM decrypted response, do extra cleanup */
  694.   if(!HTAA_PEMclose(s))
  695. #endif /* PEM_AUTH */
  696.   /* Close socket before doing free. */
  697.   NETCLOSE(s);
  698.   (*target->isa->free)(target);
  699.  
  700.   if (doing_redirect)
  701.     {
  702.       /* OK, now we've got the redirection URL temporarily stored
  703.          in external variable redirecting_url, exported from HTMIME.c,
  704.          since there's no straightforward way to do this in the library
  705.          currently.  Do the right thing. */
  706.       status = HT_REDIRECTING;
  707.     }
  708.   else
  709.     {
  710.       status = HT_LOADED;
  711.     }
  712.  
  713.   /*    Clean up
  714.    */
  715.   
  716.  clean_up: 
  717.   if (line_buffer) 
  718.     free(line_buffer);
  719.   if (line_kept_clean) 
  720.     free(line_kept_clean);
  721.  
  722.  done:
  723.   /* Clear out on exit, just in case. */
  724.   do_post = 0;
  725.   return status;
  726. }
  727.  
  728.  
  729. /*    Protocol descriptor
  730. */
  731.  
  732. PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0 };
  733.